package com.yesep.learn.design.structural.flyweight;

/**
 * 享元模式（Flyweight Pattern）是一种结构型设计模式，其主要目的是通过共享技术来有效地支持大量细粒度对象的重用，从而减少内存占用并提高程序效率。
 * 以下是对享元模式的详细解析：
 * <p>
 * 一、定义与原理
 * 享元模式通过将对象的信息分为内部状态和外部状态来工作。内部状态是存储在享元对象内部的，不会随环境的改变而有所不同，即它们是可以共享的；
 * 外部状态是根据场景而变化的，不能共享，应由客户端代码保存。享元模式通过共享内部状态来减少对象数量，从而减少内存使用。
 * <p>
 * 二、核心组件
 * 抽象享元（Flyweight）角色：是所有具体享元类的基类，为具体享元规范需要实现的公共接口。非享元的外部状态以参数的形式通过方法传入。
 * 具体享元（Concrete Flyweight）角色：实现抽象享元角色中所规定的接口。具体享元对象可以包含内部状态，但外部状态在需要时由客户端传入。
 * 非享元（Unsharable Flyweight）角色：代表那些不能共享的对象，通常是因为它们的状态在运行时是变化的，或者它们的状态是不可共享的。
 * 享元工厂（Flyweight Factory）角色：负责创建和管理享元对象。当客户对象请求一个享元对象时，享元工厂检查系统中是否存在符合要求的享元对象，
 * 如果存在则提供给客户；如果不存在的话，则创建一个新的享元对象。
 * 客户端（Client）角色：使用享元工厂来获取享元对象，并在需要时传递外部状态。客户端代码不需要创建大量的相似对象，而是通过享元工厂获取共享对象。
 * 三、优点
 * 减少对象的创建：通过共享相似对象，减少了对象的创建数量，降低了系统的内存消耗。
 * 提高系统性能：由于减少了对象的数量，系统的性能可以得到提升，尤其是在需要大量相似对象的应用场景中。
 * 增加系统的灵活性：通过将对象的内部状态和外部状态分离，系统可以更加灵活地适应不同的场景。
 * 四、缺点
 * 增加了系统的复杂性：需要分离出内部状态和外部状态，增加了系统的设计和实现难度。
 * 对外部状态的管理增加了系统的维护难度：客户端需要管理外部状态，并在需要时传递给享元对象，这可能会增加客户端代码的复杂性。
 * 潜在的线程安全问题：如果多个线程同时访问享元对象池，并且没有适当的同步机制，可能会引发线程安全问题。
 * 五、应用场景
 * 享元模式通常在以下情况下使用：
 * <p>
 * 大量相似对象：当应用程序需要创建大量相似对象时，例如字符、图标、按钮、粒子等。
 * 对象的内部状态和外部状态：如果一个对象可以分成内部状态（不变的、可共享的部分）和外部状态（变化的、不可共享的部分），则可以使用享元模式将内部状态共享，而外部状态由客户端管理。
 * 性能优化：当创建和销毁对象的成本很高时，例如数据库连接、线程池中的线程等，可以使用享元模式来重复使用这些资源，提高性能。
 * 缓存管理：享元模式可以用于实现缓存管理，例如在Web应用中缓存页面片段或数据库查询结果以提高响应速度。
 * 文本编辑器和绘图软件：在文本编辑器和绘图软件中，字符、字体、颜色、图形等都可以作为享元对象，以便在编辑大型文档或绘制复杂图形时减少内存消耗。
 * 游戏开发：在游戏开发中，粒子系统、游戏角色、装备和道具等可以作为享元对象，以节省内存和提高性能。
 * 六、示例
 * 假设我们正在开发一个文档编辑器，需要大量的字符对象。使用享元模式，我们可以共享相同的字符实例，而不是为每个出现的字符创建一个新对象。
 * 例如，可以定义一个字符享元类，其中包含字符的字型外观、字模和其他格式信息（这些作为内部状态），而字符在文档中的位置则作为外部状态由客户端管理。
 * 然后，可以创建一个享元工厂来管理这些字符享元对象的创建和重用。在客户端代码中，通过享元工厂获取字符享元对象，并设置其在文档中的位置，从而实现字符的共享和重用。
 */
public class Main {
    public static void main(String[] args) {
        String mp3 = "hello.mp3";
        String mp4 = "hello.mp4";
        MediaPlayerFactory factory = new MediaPlayerFactory();

        // 请求播放器实例
        IMediaPlayerFlyweight audioPlayer = factory.getPlayer(mp3);
        audioPlayer.play();
        audioPlayer.play();
        audioPlayer.stop();

        IMediaPlayerFlyweight videoPlayer = factory.getPlayer(mp4);
        videoPlayer.play();
        videoPlayer.play();
        videoPlayer.stop();
    }
}
